#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sqlite3.h>
#include <pthread.h>
#include "cJSON.h"
#include "MD5.h"

#define N 1024
#define TCP_ADDRESS "192.168.51.134"
#define TCP_PORT "8000"
#define TCP_UPGRADE_PORT "8001"

pthread_t tid;
int acceptfd;

int system_cmd(const char *format, ...)
{
    va_list arg;
    pid_t status;
    char cmd[N];

    va_start(arg, format);
    vsprintf(cmd, format, arg);
    va_end(arg);

    status = system(cmd);
    if (-1 == status || !WIFEXITED(status) || 0 != WEXITSTATUS(status))
    {
        printf("[%s]: FAIL", cmd);
        return -1;
    }
    else
    {
        return 0;
    }
}

void *upgrade_thread(void *arg)
{
    // 解析升级包信息
    cJSON *root = (cJSON *)arg;
    cJSON *data = cJSON_GetObjectItem(root, "data");
    cJSON *version = cJSON_GetObjectItem(data, "ver");
    cJSON *fname = cJSON_GetObjectItem(data, "file_name");
    cJSON *flen = cJSON_GetObjectItem(data, "file_len");
    cJSON *md5 = cJSON_GetObjectItem(data, "md5");
    // 为升级包新建文件
    FILE *fp = fopen(fname->valuestring, "w+");
    if (fp == NULL)
    {
        perror("upgrade new file fopen err");
        return -1;
    }

    /********************************************************创建数据传输的连接(8001)****************************************************/

    int upgradefd = socket(AF_INET, SOCK_STREAM, 0);
    if (upgradefd < 0)
    {
        perror("upgrade socket err");
        return -1;
    }
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(TCP_UPGRADE_PORT)); // 端口号
    addr.sin_addr.s_addr = inet_addr(TCP_ADDRESS);

    if (bind(upgradefd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("upgrade bind err");
        return -1;
    }
    else
    {
        printf("OTA连接绑定成功\n");
    }

    if (listen(upgradefd, 10) < 0)
    {
        perror("upgrade listen err");
        return -1;
    }
    else
    {
        printf("OTA连接监听中\n");
    }

    int acceptfd1;
    char buf[1024] = "";
    int ret, filesize;
    struct sockaddr_in caddr;
    int len = sizeof(caddr); // len是记录客户信息的结构体的大小

    /*阻塞等待接收客户端的连接请求,并将连接成功的客户端信息写入到结构体变量caddr中*/
    acceptfd1 = accept(upgradefd, (struct sockaddr *)&caddr, &len);
    if (acceptfd1 < 0)
    {
        printf("accept err");
        return -1;
    }
    else
    {
        printf("等待接收升级包\n");
    }

    // printf("客户IP:%s 端口号：%d \n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));

    while (1)
    {
        memset(buf, 0, 1024);
        /*接收消息*/
        ret = recv(acceptfd1, buf, 1024, 0);
        if (ret < 0)
        {
            perror("recv err");
            return -1;
        }
        else if (ret == 0)
        {
            printf("升级包接收完成\n");
            fclose(fp);
            break;
        }
        else
        {
            fwrite(buf, 1, ret, fp);
            printf("接收数据：%s\n", buf);
            filesize += ret;
            fflush(NULL);
        }
    }
    printf("filesize = %d\n", filesize);
    cJSON *ansroot;
    uint8_t digest[16];
    MD5_File(fname->valuestring, digest);
    char md5buf[33];
    MD5_ToString(digest, md5buf);
    printf("%s", md5buf);

    if (filesize == flen->valueint && strcmp(md5buf, md5->valuestring) == 0)
    {
        printf("升级包传输成功\n");
        ansroot = cJSON_CreateObject();
        cJSON *type = cJSON_CreateNumber(2);
        cJSON_AddItemToObject(ansroot, "type", type);
        cJSON *data = cJSON_CreateObject();
        cJSON_AddItemToObject(ansroot, "data", data);
        cJSON *trans_success = cJSON_CreateTrue();
        cJSON_AddItemToObject(data, "trans_success", trans_success);
        char *buffer = cJSON_Print(ansroot);
        send(acceptfd, buffer, strlen(buffer) + 1, 0);
    }
    else
    {
        printf("升级包传输失败\n");
        ansroot = cJSON_CreateObject();
        cJSON *type = cJSON_CreateNumber(2);
        cJSON_AddItemToObject(ansroot, "type", type);
        cJSON *data = cJSON_CreateObject();
        cJSON_AddItemToObject(ansroot, "data", data);
        cJSON *trans_success = cJSON_CreateFalse();
        cJSON_AddItemToObject(data, "trans_success", trans_success);
        char *buffer = cJSON_Print(ansroot);
        send(acceptfd, buffer, strlen(buffer) + 1, 0);
        return NULL;
    }

    char cmd[128];
    sprintf(cmd, "unzip /home/hq/device_find/%s", fname->valuestring);
    printf("cmd = %s\n", cmd);

    if (system(cmd) == 0)
    {
        printf("升级成功\n");
        ansroot = cJSON_CreateObject();
        cJSON *type = cJSON_CreateNumber(3);
        cJSON_AddItemToObject(ansroot, "type", type);
        cJSON *data = cJSON_CreateObject();
        cJSON_AddItemToObject(ansroot, "data", data);
        cJSON *update_success = cJSON_CreateTrue();
        cJSON_AddItemToObject(data, "update_success", update_success);
        char *buffer = cJSON_Print(ansroot);
        send(acceptfd, buffer, strlen(buffer) + 1, 0);

        FILE *fpjson = fopen("node.json", "r+");
        if (fpjson == NULL)
        {
            perror("fopen err");
            return -1;
        }

        fseek(fpjson, 1, SEEK_SET);
        char buff[32];
        char buff1[100];
        strcpy(buff, version->valuestring);
        sprintf(buff1, "	\"version\": \"%s\",\n", buff);
        fputs(buff1, fpjson);
        fclose(fpjson);
    }
    else
    {
        printf("升级失败\n");
        ansroot = cJSON_CreateObject();
        cJSON *type = cJSON_CreateNumber(3);
        cJSON_AddItemToObject(ansroot, "type", type);
        cJSON *data = cJSON_CreateObject();
        cJSON_AddItemToObject(ansroot, "data", data);
        cJSON *update_success = cJSON_CreateFalse();
        cJSON_AddItemToObject(data, "update_success", update_success);

        char *buffer = cJSON_Print(ansroot);
        send(acceptfd, buffer, strlen(buffer) + 1, 0);
    }

    cJSON_Delete(ansroot);

    return NULL;
}

int main(int argc, char const *argv[])
{
    int broadfd;

    // 创建一个socket文件描述符
    broadfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (broadfd < 0)
    {
        perror("sock err");
        return -1;
    }

    // 绑定套接字(ip+port)
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(TCP_PORT)); // 端口号
    addr.sin_addr.s_addr = INADDR_ANY;

    int addrlen = sizeof(addr);

    if (bind(broadfd, (struct sockaddr *)&addr, addrlen) < 0)
    {
        perror("bind err");
        return -1;
    }

    int len;
    char buf[N] = {0};
    struct sockaddr_in cliaddr;

    // 接收广播包
    bzero(buf, N);
    len = recvfrom(broadfd, buf, N, 0, (struct sockaddr *)&cliaddr, &addrlen);
    if (len < 0)
    {
        perror("recvfrom err");
        return -1;
    }

    cJSON *root = cJSON_Parse(buf);
    // 一定要判断
    if (NULL == root)
    {
        printf("parse err\n");
        cJSON_Delete(root);
        return -1;
    }

    // 反序列化
    cJSON *type = cJSON_GetObjectItem(root, "type");
    cJSON *request = cJSON_GetObjectItem(root, "request");
    if (type->valueint != 0 || strcmp(request->valuestring, "connect_info") != 0)
    {
        printf("not to find me\n");
        cJSON_Delete(root);
        return -1;
    }
    printf("type = %d, request = %s\n", type->valueint, request->valuestring);

    cJSON_Delete(root);

    // 序列化
    root = cJSON_CreateObject();
    type = cJSON_CreateNumber(0);
    cJSON_AddItemToObject(root, "type", type);
    cJSON *data = cJSON_CreateObject();
    cJSON *tcp_address = cJSON_CreateString(TCP_ADDRESS);
    cJSON_AddItemToObject(data, "tcp_address", tcp_address);
    cJSON *tcp_port = cJSON_CreateString(TCP_PORT);
    cJSON_AddItemToObject(data, "tcp_port", tcp_port);
    cJSON_AddItemToObject(root, "data", data);

    char *buffer = cJSON_Print(root);

    // 回复QT客户端自己的IP和端口
    sendto(broadfd, buffer, strlen(buffer) + 1, 0, (struct sockaddr *)&cliaddr, addrlen);
    printf("%s\n", buffer);

    cJSON_Delete(root);
    buffer = NULL;

    /********************************************************创建命令传输的连接(8000)*****************************************************/

    int tcpfd = socket(AF_INET, SOCK_STREAM, 0);
    if (tcpfd < 0)
    {
        perror("sock err");
        return -1;
    }

    if (bind(tcpfd, (struct sockaddr *)&addr, addrlen) < 0)
    {
        perror("bind err");
        return -1;
    }
    else
    {
        printf("绑定套接字成功\n");
    }
    // 监听套接字
    if (listen(tcpfd, 5) < 0)
    {
        perror("listen err");
        return -1;
    }
    else
    {
        printf("监听中\n");
    }

    char buf1[N] = {0};
    int ret;

    // 定义一个结构体变量来存接收到的客户信息
    struct sockaddr_in caddr;
    len = sizeof(caddr); // len是记录客户信息的结构体的大小

    // 创建和打开数据库
    sqlite3 *db;
    if (sqlite3_open("user.db", &db) != SQLITE_OK)
    {
        fprintf(stderr, "open err:%s\n", sqlite3_errmsg(db));
        return -1;
    }
    else
    {
        printf("打开数据库成功\n");
    }
    // 创建表
    char *errmsg;

    if (sqlite3_exec(db, "create table if not exists usermsg (account string,password string)", NULL, NULL, &errmsg) != SQLITE_OK)

    {
        fprintf(stderr, "create table err:%s\n", errmsg);
        return -1;
    }
    else
    {
        printf("打开表成功\n");
    }

    char sql[64] = "";
    char **result = NULL;    // 用于存储查询到的结果
    int row = 0, column = 0; // 记录行数和列数
    while (1)
    {
        /*阻塞等待接收客户端的连接请求,并将连接成功的客户端信息写入到结构体变量caddr中*/
        acceptfd = accept(tcpfd, (struct sockaddr *)&caddr, &len);
        if (acceptfd < 0)
        {
            printf("accept err");
            return -1;
        }
        else
        {
            printf("等待接收客户端请求\n");
        }

        printf("客户IP:%s 端口号：%d \n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));

        while (1)
        {
            memset(buf1, 0, sizeof(buf1));

            /*接收消息*/
            ret = recv(acceptfd, buf1, sizeof(buf1), 0); // 接收命令

            if (ret < 0)
            {
                perror("recv err");
                cJSON_Delete(root);
                return -1;
            }
            else if (ret == 0)
            {
                printf("客户退出\n");
                break;
            }
            else
            {
                cJSON *root = cJSON_Parse(buf1);
                // 一定要判断
                if (NULL == root)
                {
                    printf("parse err\n");
                    cJSON_Delete(root);
                    return -1;
                }
                cJSON *type = cJSON_GetObjectItem(root, "type");
                // type==4 --->登录注册

                if (type->valueint == 4)
                {
                    cJSON *order = cJSON_GetObjectItem(root, "order");
                    cJSON *user = cJSON_GetObjectItem(root, "user");
                    cJSON *password = cJSON_GetObjectItem(root, "password");
                    if (order->valueint == 0)
                    {
                        sprintf(sql, "insert into usermsg values('%s','%s')", user->valuestring, password->valuestring);

                        if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
                        {
                            fprintf(stderr, "insert err:%s\n", errmsg);
                            cJSON_Delete(root);
                            return -1;
                        }
                        else
                        {
                            printf("插入成功\n");
                        }
                    }
                    else if (order->valueint == 1)
                    {
                        sprintf(sql, "select * from usermsg where account='%s' and password='%s'", user->valuestring, password->valuestring);
                        sqlite3_get_table(db, sql, &result, &row, &column, &errmsg);
                        root = cJSON_CreateObject();
                        cJSON *ans;
                        if (row == 0)
                        {
                            printf("登录失败\n");
                            ans = cJSON_CreateString("no");
                        }
                        else
                        {
                            printf("登录成功\n");
                            ans = cJSON_CreateString("yes");
                        }
                        cJSON_AddItemToObject(root, "answer", ans);
                        buffer = cJSON_Print(root);
                        send(acceptfd, buffer, strlen(buffer) + 1, 0);
                    }
                    memset(buf1, 0, sizeof(buf1));
                }

                // type==1 询问是否升级
                if (type->valueint == 1)
                {
                    // 取出点表里的版本号
                    FILE *fp = fopen("node.json", "r");
                    if (fp == NULL)
                    {
                        perror("fopen err");
                        return -1;
                    }
                    char buf[3000] = {0};
                    size_t len = fread(buf, 1, 3000, fp);
                    if (len <= 0)
                    {
                        perror("read err");
                        return -1;
                    }
                    cJSON *node = cJSON_Parse(buf);
                    cJSON *node_version = cJSON_GetObjectItem(node, "version");
                    // 取出QT端给的版本号
                    cJSON *data0 = cJSON_GetObjectItem(root, "data");
                    cJSON *version = cJSON_GetObjectItem(data0, "ver");

                    // 给QT端的回复
                    cJSON *ansroot = cJSON_CreateObject();
                    cJSON *type = cJSON_CreateNumber(1);
                    cJSON_AddItemToObject(ansroot, "type", type);
                    cJSON *data = cJSON_CreateObject();
                    cJSON_AddItemToObject(ansroot, "data", data);

                    if (strcmp(version->valuestring, node_version->valuestring) == 0)
                    {
                        printf("版本一致，无需升级\n");

                        cJSON *update = cJSON_CreateFalse();
                        cJSON_AddItemToObject(data, "update", update);

                        buffer = cJSON_Print(ansroot);
                        printf("%s\n", buffer);
                        send(acceptfd, buffer, strlen(buffer) + 1, 0); // 不用升级
                        cJSON_Delete(ansroot);
                    }
                    else
                    {

                        printf("版本不一致,需要升级\n");

                        cJSON *update = cJSON_CreateTrue();
                        cJSON_AddItemToObject(data, "update", update);
                        cJSON *tcp_address = cJSON_CreateString(TCP_ADDRESS);
                        cJSON_AddItemToObject(data, "tcp_address", tcp_address);
                        cJSON *tcp_port = cJSON_CreateString(TCP_UPGRADE_PORT);
                        cJSON_AddItemToObject(data, "tcp_port", tcp_port);
                        buffer = cJSON_Print(ansroot);

                        printf("%s\n", buffer);
                        send(acceptfd, buffer, strlen(buffer) + 1, 0); // 要升级
                        printf("555\n");

                        cJSON_Delete(ansroot);

                        pthread_create(&tid, NULL, upgrade_thread, root);
                        pthread_detach(tid);
                    }

                    fclose(fp);
                }
            }
        }

        close(acceptfd);
    }

    cJSON_Delete(root);
    /* 关闭套接字 */
    close(tcpfd);
    sqlite3_close(db);

    return 0;
}
